/* 
 * Copyright 2011 kOSMik.
 * This file is part of kOSMik.
 * 
 * kOSMik is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * kOSMik is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with kOSMik.  If not, see <http://www.gnu.org/licenses/>.
 *  
 * kOSMik:
 * Open Street Map - Editor.
 * http://code.google.com/p/swp-dv-ws2010-osm-2/
 */

package de.fuberlin.kosmik.gui.activity;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import de.fuberlin.kosmik.R;
import de.fuberlin.kosmik.data.DataBaseConnector;
import de.fuberlin.kosmik.data.dao.TrackRecord;
import de.fuberlin.kosmik.data.osmt.OsmtDataFactory;
import de.fuberlin.kosmik.export.GpxExporter;
import de.fuberlin.kosmik.gui.dialog.Dialogs;
import de.fuberlin.kosmik.gui.view.adapter.TrackListAdapter;
import de.fuberlin.kosmik.gui.view.adapter.TrackListAdapter.TrackListItem;
import de.fuberlin.kosmik.gui.view.adapter.TrackListAdapter.ViewHolderTrack;
import de.fuberlin.kosmik.gui.view.adapter.ViewHolder;
import de.fuberlin.kosmik.log.Constants;
import de.fuberlin.kosmik.trackrecorder.TrackRecorderService;

/**
 * This activity is called first if the application starts. The StartActivity
 * provides elementary track operations like create a new track or delete,
 * rename, edit and show a recorded track. Furthermore the export of recorded
 * track data is also provided.
 */
public class StartActivity extends TrackActivity implements OnClickListener,
		OnItemClickListener {

	private static final int INTENT_VIEW = 1;

	/**
	 * TRACK_NAME is a message-flag send to the TrackRecorder-Service to
	 * initiate recording of a new track.
	 */
	public static final String TRACK_NAME = "TRACK_NAME";

	private ListView lstRecordedTracks;
	/**
	 * Reference to the listAdapter.
	 */
	TrackListAdapter listAdapter;

	private TextView actionBarTitle;
	private ImageView actionBarSeparator;
	private ImageButton btnAddTrack;
	private ImageButton btnSearch;

	private boolean inSearchMode;
	/**
	 * Reference to the search box in the action bar.
	 */
	EditText actionBarSearchBox;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		setTitle(R.string.start__title);
		super.onCreate(savedInstanceState);
		setContentView(R.layout.start);
		// find all Views of the activity.
		findViews();
		// setup adapter and show all recorded tracks.
		refreshTracklist();

		// if activity was restarted, restore search box with its content.
		if (savedInstanceState != null) {
			inSearchMode = savedInstanceState.getBoolean("inSearchMode", false);
			if (inSearchMode) {
				showSearchBox();
			}

		}

		// set all ClickListeners of the buttons and set TextWatcher of the
		// search box.
		setClickListenersAndTextWatcher();

		// install database if this app is called the first time
		installOsmtDatabase();

	}

	@Override
	public void onStart() {
		super.onStart();
		listAdapter.refreshTracks();
	}

	private void installOsmtDatabase() {

		class DatabaseInstallerTask extends AsyncTask<Void, Void, Void> {
			private ProgressDialog dialog;

			@Override
			protected void onPreExecute() {
				super.onPreExecute();
				dialog = ProgressDialog.show(StartActivity.this, "Setup Database",
						"Please wait...", true);
			}

			@Override
			protected Void doInBackground(Void... params) {
				OsmtDataFactory.createOsmtData(StartActivity.this);
				return null;
			}

			@Override
			protected void onPostExecute(Void result) {
				super.onPostExecute(result);
				dialog.dismiss();
			}

		}

		new DatabaseInstallerTask().execute();
	}

	@Override
	protected void onSaveInstanceState(Bundle outState) {
		super.onSaveInstanceState(outState);
		// save search mode and search word for restoring the state after
		// orientation change.
		outState.putBoolean("inSearchMode", inSearchMode);
	}

	@Override
	public void onClick(View v) {
		switch (v.getId()) {
		case R.id.action_bar_add_track_button:
			tryStartServiceAndNewTrack();
			break;
		case R.id.action_bar_search_button:
			if (inSearchMode)
				hideSearchBox();
			else
				showSearchBox();
			break;

		default:
			break;
		}
	}

	@Override
	public void onItemClick(AdapterView<?> listView, View itemView, int position, long id) {
		if (!((ViewHolder<?>) itemView.getTag()).isHeader()) {
			long selectedTrackId = ((ViewHolderTrack) itemView.getTag()).getItem().trackRecord.trackId;
			showTrack(selectedTrackId);
		}
	}

	private void findViews() {
		lstRecordedTracks = (ListView) findViewById(R.id.start_listRecordedTracks);
		btnAddTrack = (ImageButton) findViewById(R.id.action_bar_add_track_button);
		btnSearch = (ImageButton) findViewById(R.id.action_bar_search_button);
		actionBarTitle = (TextView) findViewById(R.id.action_bar_title);
		actionBarSeparator = (ImageView) findViewById(R.id.action_bar_separator);
		actionBarSearchBox = (EditText) findViewById(R.id.action_bar_search_box);
	}

	private void setClickListenersAndTextWatcher() {
		lstRecordedTracks.setOnItemClickListener(this);
		registerForContextMenu(lstRecordedTracks);
		btnAddTrack.setOnClickListener(this);
		btnSearch.setOnClickListener(this);
		actionBarSearchBox.addTextChangedListener(listAdapter);
	}

	/**
	 * Shows search box in the action bar and hides all other buttons.
	 */
	private void showSearchBox() {
		actionBarTitle.setVisibility(View.GONE);
		btnAddTrack.setVisibility(View.GONE);
		actionBarSeparator.setVisibility(View.GONE);
		actionBarSearchBox.setVisibility(View.VISIBLE);
		actionBarSearchBox.requestFocus();
		btnSearch.setImageResource(R.drawable.ic_action_bar_search_close);
		inSearchMode = true;
	}

	/**
	 * Hides search box in the action bar and shows all other buttons.
	 */
	private void hideSearchBox() {
		actionBarSearchBox.setVisibility(View.GONE);
		actionBarTitle.setVisibility(View.VISIBLE);
		btnAddTrack.setVisibility(View.VISIBLE);
		actionBarSeparator.setVisibility(View.VISIBLE);
		btnSearch.setImageResource(R.drawable.ic_action_bar_search);
		actionBarSearchBox.setText("");
		listAdapter.resetSearch();
		inSearchMode = false;
	}

	/**
	 * Setup options menu.
	 */
	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// set optionsmenu defined in res.menu.start
		getMenuInflater().inflate(R.menu.start, menu);
		return super.onCreateOptionsMenu(menu);
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {

		Log.d(Constants.TAG, "pressed" + item.getItemId());
		switch (item.getItemId()) {
		case R.id.opt_addNewTrack:
			Log.d(Constants.TAG, "pressed button");
			tryStartServiceAndNewTrack();
			break;
		case R.id.opt_editSettings:
			Log.d(Constants.TAG, "pressed button");
			final Intent intent = new Intent(this, SettingsActivity.class);
			startActivity(intent);
			break;
		case R.id.opt_exitApplication:
			Log.d(Constants.TAG, "exit application");
			this.finish();
			break;
		case R.id.opt_About:
			Log.d(Constants.TAG, "show about screen");
			final Intent i = new Intent(this, AboutActivity.class);
			startActivity(i);
			break;
		default:
			break;
		}

		return super.onOptionsItemSelected(item);
	}

	/**
	 * Called, when the context menu pop up.
	 */
	@Override
	public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo) {

		// create context menu for a selected track in lstRecordedTracks
		if (view == lstRecordedTracks) {
			// get the view of the item that the event occur
			AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo;
			final View itemView = info.targetView;
			// get the payload, that contains the track name
			if (!((ViewHolder<?>) itemView.getTag()).isHeader()) {
				TrackListItem listItem = ((ViewHolderTrack) itemView.getTag()).getItem();
				// set context menu title
				CharSequence selectedTrackName = listItem.trackRecord.trackName;
				menu.setHeaderTitle(selectedTrackName);
				// inflate context menu options
				getMenuInflater().inflate(R.menu.start_track_context, menu);
			}

		}

		super.onCreateContextMenu(menu, view, menuInfo);
	}

	@Override
	public boolean onContextItemSelected(MenuItem item) {

		AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item
				.getMenuInfo();
		View itemView = info.targetView;
		TrackListItem listItem = ((ViewHolderTrack) itemView.getTag()).getItem();
		long trackId = listItem.trackRecord.trackId;

		switch (item.getItemId()) {
		case R.id.opt_showTrack:
			showTrack(trackId);
			break;
		case R.id.opt_editTrack:
			editTrack(trackId);
			break;
		case R.id.opt_renameTrack:
			renameTrack(trackId);
			break;
		case R.id.opt_deleteTrack:
			deleteTrack(trackId);
			break;
		default:
			new GpxExporter(this).execute(Long.valueOf(trackId));
			break;
		}
		return super.onContextItemSelected(item);
	}

	/**
	 * Starts ViewActivity.
	 */
	void switchToViewActivity() {
		final Intent viewActivityIntent = new Intent(this, ViewActivity.class);
		startActivityForResult(viewActivityIntent, INTENT_VIEW);
	}

	@Override
	protected void onActivityResult(int requestCode, int resultCode, Intent data) {

		if (requestCode == INTENT_VIEW) {
			// and refresh tracklist
			refreshTracklist();
		}
		super.onActivityResult(requestCode, resultCode, data);
	}

	/**
	 * Invoke START_TRACK by the TrackRecorder-Service to initiate recording of
	 * a new track.
	 */
	void newTrack() {
		Log.d(Constants.TAG, "method call startTrack");
		// generate a track name and call startTrack method of the service.
		getTrackService().startTrack(suggestTrackName());
	}

	/**
	 * Try to start service to start a new track. But starts a track only if gps
	 * signal is detected. Otherwise inform user about missing signal
	 */
	void tryStartServiceAndNewTrack() {
		// start service
		TrackRecorderService.startService(this);
		// show ProgressDialog to inform user, that he has to wait till service
		// is
		// started
		final ProgressDialog dialog = ProgressDialog.show(this, "",
				"Starting Service. Please wait...", true);
		// handler checks if service is running and continues then to start new
		// Track and switch to ViewActivity
		Handler handler = new Handler();
		handler.postDelayed(new Runnable() {

			@Override
			public void run() {
				if (TrackRecorderService.getService(StartActivity.this) != null) {
					dialog.dismiss();
					if (getTrackService().isGpsEnabled()) {
						newTrack();
						switchToViewActivity();
					}
					// if gps is not enabled, pop up a message
					else {
						Dialogs.confirmMessage(
								getString(R.string.start__message__no_gps_available),
								StartActivity.this);
					}
				} else {
					tryStartServiceAndNewTrack();
				}

			}
		}, 50);

	}

	/**
	 * Make a suggestion for a temporary track name. This is implemented as the
	 * String representation of the start time: dd.MM.yy-HH:mm;ss.
	 * 
	 * @return a String of the default name of the track.
	 */
	private static String suggestTrackName() {
		// get current timestamp as text
		DateFormat dateFormat = new SimpleDateFormat("dd.MM.yy-HH:mm:ss");
		String currentTime = dateFormat.format(new Date());

		return "Track_" + currentTime;
	}

	/**
	 * Updates content of track list, initialize adapter if not happened yet.
	 */
	void refreshTracklist() {
		if (listAdapter == null) {
			listAdapter = new TrackListAdapter(this);
		}
		lstRecordedTracks.setAdapter(listAdapter);
		listAdapter.refreshTracks();
	}

	/**
	 * Edits the selected track.
	 * 
	 * @param selectedTrackID id of the selected track.
	 */
	private void editTrack(final long selectedTrackID) {
		Intent intent = new Intent(StartActivity.this, TrackInfoActivity.class);
		intent.putExtra(TrackInfoActivity.TRACK_ID, selectedTrackID);
		startActivity(intent);
	}

	private void deleteTrack(final long trackId) {
		final DataBaseConnector dbc = getDataBase();
		final String trackName = dbc.getNameFromTrack(trackId);
		final String msgConfirmRemovingTrack = String.format(
				getString(R.string.dialog__message__confirm_removing_track), trackName);
		final String msgInfoTrackRemovedSuccessfully = String.format(
				getString(R.string.toast__message__track_removed_successfully), trackName);
		final String msgInfoRemovingTagFailed = String.format(
				getString(R.string.toast__message__removing_track_failed), trackName);

		Dialogs.confirmationDialog(StartActivity.this, msgConfirmRemovingTrack,
				new Dialog.OnClickListener() {

					@Override
					public void onClick(DialogInterface dialog, int which) {

						if (dbc.removeTrack(trackId)) {
							refreshTracklist();
							showToast(msgInfoTrackRemovedSuccessfully);
						} else {
							showToast(msgInfoRemovingTagFailed);
						}

					}
				});
	}

	/**
	 * NewTrackNameView has protected visibility for reasons of optimization.
	 * Used in renameTrack;
	 */
	protected EditText newTrackNameView;

	private void renameTrack(final long trackId) {
		final DataBaseConnector dataBase = getDataBase();
		final TrackRecord trackRecord = dataBase.getTrackById(trackId);
		final String[] autoCompleteSuggestions = dataBase.getAllTrackNames();
		final String oldTrackName = trackRecord.trackName;
		final String msgAskRemovingTrack = String.format(
				getString(R.string.dialog__message__rename_track), oldTrackName);

		// create a dialog to obtain users ideal name
		newTrackNameView = Dialogs.editDialog(StartActivity.this, msgAskRemovingTrack,
				oldTrackName, autoCompleteSuggestions, new Dialog.OnClickListener() {

					@Override
					public void onClick(DialogInterface dialog, int which) {
						// rename the track
						final String newTrackName = newTrackNameView.getText().toString();
						dataBase.setTrackName(trackId, newTrackName);
						refreshTracklist();
					}
				});
	}

	/**
	 * Shows the selected track.
	 * 
	 * @param trackId of the selected Track.
	 */
	private void showTrack(long trackId) {
		Intent intent = new Intent(StartActivity.this, ShowSavedTrackActivity.class);
		intent.putExtra(ShowSavedTrackActivity.TRACK_ID, trackId);
		startActivity(intent);
	}

	/**
	 * Shows a Toast with the given message.
	 * 
	 * @param message of the Toast.
	 */
	void showToast(String message) {
		Toast.makeText(StartActivity.this, message, Toast.LENGTH_LONG).show();
	}

}
